#ifndef TBS_CPP_MATCH_HPP
#define TBS_CPP_MATCH_HPP

#include <typeinfo>
#include <base/type_check.h>

namespace {
struct MatchStruct {
public:
  template<typename V, typename T>
  bool operator()(V q, T i);

};

template<typename M>
struct MatcherStruct {
public:
  bool _matched = false;
  M _method;

  template<typename V, typename T>
  bool operator()(V q, T i);
};

#include "inls/base/matchStruct.cuh"

}
namespace tbs {
class equals_match_expression;

class any_match_expression;

template<typename F>
class expression_match_expression {
private:
  F _f;
public:
  explicit expression_match_expression(F f);

  template<typename V, typename I>
  bool test(V target, I test) CONST;
};

#include "inls/base/matchExpressions.cuh"

template<typename V, typename F, typename E>
class Matcher {
private:
  V _val;
  E _eq;
  F _fun;
public:
  Matcher(V v, F f, E eq = E());

  template<typename I>
  bool test(I item) CONST;

  template<typename I>
  bool operator==(I item) CONST;

  template<typename I>
  void trigger(I item);

  template<typename I>
  void operator()(I item);
};

#include "inls/base/matcher.cuh"

template<typename F>
using AnyMatcher = Matcher<std::nullptr_t, F, any_match_expression>;

template<typename V, typename F>
using EqualsMatcher = Matcher<V, F, equals_match_expression>;

template<typename F, typename U>
using ExpressionMatcher = Matcher<std::nullptr_t, F, expression_match_expression<U>>;

template<typename V, typename F>
static inline EqualsMatcher<V, F> make_matcher(const V &v, F f) {
  return EqualsMatcher<V, F>(v, f);
}

template<typename F, typename U>
static inline ExpressionMatcher<F, U> make_exp_matcher(U u, F f) {
  return ExpressionMatcher<F, U>(nullptr, f, expression_match_expression(u));
}

template<typename F>
static inline AnyMatcher<F> make_any_matcher(F f) {
  return AnyMatcher<F>(nullptr, f);
}

#define OTHER_CASE_MATCH(f) tbs::make_any_matcher([&](auto _val){f})

#define CASE_MATCH(v, f) tbs::make_matcher(v,[&](auto _val){f})

#define CASE_EXP_MATCH(e, f) tbs::make_exp_matcher([&](){ return e;},[&](auto _val){f})

template<typename V, typename ...T>
void match_any(V v, T... t) {
  MatchStruct matchStruct;
  expandArgs(matchStruct, v, t...);
}

#define MATCH(target, ...) tbs::match_any(target,__VA_ARGS__);

template<typename V, typename MATCHED, typename UN_MATCH, typename ...Args>
void match_with(V target, MATCHED matched, UN_MATCH unMatch, Args ...args) {
  MatcherStruct<MATCHED> matchStruct;
  if (matched != nullptr) {
	matchStruct._method = matched;
  }
  expandArgs(matchStruct, target, args...);
  if (!matchStruct._matched && unMatch != nullptr) {
	unMatch(target);
  }
}

template<typename V, typename MATCHED, typename ...Args>
bool match(V target, MATCHED matched, Args ...args) {
  MatcherStruct<MATCHED> matchStruct;
  if (matched != nullptr) {
	matchStruct._method = matched;
  }
  expandArgs(matchStruct, target, args...);
  return matchStruct._matched;
}

}

#endif // !TBS_CPP_MATCH_HPP
